package sun.scheduler.gui;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.*;

/**
 * A {
 *
 * @see JPanel that plots curves into a coordinate system based on a {
 * @see Collection of {
 * @see Coordinates .
 * @author mfj
 */
public class PlotPanel extends JPanel implements MouseMotionListener, MouseListener {

    private Point selectedP = null, selectedPreal = null;
    private String summary = "";
    private double scaleX = 1;
    private double scaleY = 1;
    private int offsetX = 20;
    private int offsetY = 20;
    float maxCoord = 0, maxX = 0, maxY = 0, minX = Float.MAX_VALUE, minY = Float.MAX_VALUE;
    private double deckungsbetrag;
    private ArrayList<Curve> curves, curvesClone;

    public PlotPanel() {
        super();
        curves = new ArrayList<Curve>();
        curvesClone = new ArrayList<Curve>();
        this.addMouseMotionListener(this);
        this.addMouseListener(this);
        this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
    }

    public PlotPanel(Collection<Curve> curves) {
        super();
        this.curves = (ArrayList<Curve>) curves;
        curvesClone = new ArrayList<Curve>();
        for (Curve curve : curves) {
            curvesClone.add(curve.clone());
        }
        this.addMouseMotionListener(this);
        this.addMouseListener(this);
        this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
    }

    public Collection<Curve> cloneCurves(Collection<Curve> curves) {
        Collection<Curve> dummyThingy = new ArrayList<Curve>();
        for (Curve curve : curves) {
            dummyThingy.add(curve.clone());
        }
        return dummyThingy;
    }

    public double getDeckungsBetrag() {
        return deckungsbetrag;
    }

    public void setDeckungsBetrag(double betrag) {
        if (betrag < 0) {
            throw new IllegalArgumentException("Negative DeckungsBetrag");
        } else {
            deckungsbetrag = betrag;
        }

    }

    public void setCurves(Collection<Curve> curves) {
        this.curves = (ArrayList<Curve>) curves;
        curvesClone = new ArrayList<Curve>();
        for (Curve curve : curves) {
            curvesClone.add(curve.clone());
        }
    }

    public double getOffsetX() {
        return offsetX;
    }

    public void setOffsetX(int offsetX) {
        this.offsetX = offsetX;
    }

    public int getOffsetY() {
        return offsetY;
    }

    public void setOffsetY(int offsetY) {
        this.offsetY = offsetY;
    }

    public String getSummary() {
        return summary;
    }

    public double getScaleX() {
        return scaleX;
    }

    public void setScaleX(int scaleX) {
        if (0 >= scaleX) {
            throw new IllegalArgumentException("Negative scale");
        }

        if (this.scaleX == scaleX) {
            return;
        }

        this.scaleX = scaleX;
    }

    public double getScaleY() {
        return scaleY;
    }

    public void setScaleY(double scaleY) {
        if (0 >= scaleY) {
            throw new IllegalArgumentException("Negative scale");
        }

        if (this.scaleY == scaleY) {
            return;
        }

        this.scaleY = scaleY;
    }

    public void moveX(int delta) {
        offsetX += delta;
    }

    public void moveY(int delta) {
        offsetY += delta;
    }

    public double newX(double x) {
        return ((x - offsetX / 2) / scaleX);
    }

    public double newY(double y) {
        return ((this.getHeight() - y - offsetY / 2) / scaleY);
    }
    
    public Point getSelectedP() {
        return selectedPreal;
    }

    public int getSelectedPId() {
        int counter = 0;
        if (selectedPreal != null) {
            for (Curve c : curves) {
                counter = 0;
                for (Point p : c.getCoordinates()) {
                    if (p.equals(selectedPreal)) {
                        return counter;
                    }
                    counter++;
                }
                counter = 0;
            }
        }

        return counter;
    }

    public void calculateScale() {
        maxX = 0;
        maxY = 0;
        minX = Float.MAX_VALUE;
        minY = Float.MAX_VALUE;
        for (Curve curve : curves) {
            for (Point p : curve.getCoordinates()) {
                // maxima
                if (p.getX() > maxX) {
                    maxX = (float) p.getX();
                }
                if (p.getY() > maxY) {
                    maxY = (float) p.getY();
                }

                // minima
                if (p.getX() < minX) {
                    minX = (float) p.getX();
                }
                if (p.getY() < minY) {
                    minY = (float) p.getY();
                }

            }
        }
        float diffX = (maxX - minX), diffY = (maxY - minY), diff = diffX;
        if (diffX > diffY) {
            diff = diffY;
        }
//        if (maxX > maxY) { // proportional scale
        //maxX = maxX + maxX / 10;
        scaleX = (double) (getWidth() - offsetX) / diffX;
        scaleY = (double) (getHeight() - offsetY) / diffY;
//            maxY = maxX;
//        } else {
//            maxY = maxY+maxY/10;
//            scaleX = (double) (getWidth() - offsetX) / maxY;
//            scaleY = (double) (getHeight() - offsetY) / maxY;
//            maxX = maxY;
//        }
    }

    @Override
    protected void paintComponent(Graphics grphcs) {
        Graphics2D g2d = (Graphics2D) grphcs;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        Font font = new Font("Arial", Font.PLAIN, 10);
        g2d.setFont(font);
        super.paintComponent(g2d);

        curvesClone = (ArrayList<Curve>) cloneCurves(curves);
        calculateScale();
        int width = getWidth();
        int height = getHeight();
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);


        // draw graphs
        for (Curve curve : curvesClone) {// draw each curve individually
            curve.setScaleX(scaleX);
            curve.setScaleY(scaleY);
            curve.draw(g2d, offsetX / 2, offsetY / 2, height, width, maxY, maxX, minY, minX);
        }

        // Draw Coords
        g2d.setColor(Color.GRAY);
        double maxXc = width - offsetX / 2;
        if (maxX != 0) {
            maxXc = maxX * scaleX + offsetX / 2;
        }
        double maxYc = height - offsetY / 2;
        if (maxY != 0) {
            maxYc = maxY * scaleY + offsetY / 2;
        }
        double minXc = minX * scaleX;

        // X-axis
        g2d.draw(new Line2D.Double(offsetX / 4, height - offsetY / 4, width - offsetX / 2,
                height - offsetY / 4));

        // Y-axis
        g2d.draw(new Line2D.Double(offsetX / 4, height - offsetY / 4,
                offsetX / 4, offsetY / 2));
        if (maxX != 0 || maxY != 0) {
            for (int k = 1; k < 5; k++) {
                double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
                //  on the X-axis
                x1 = this.getWidth() - offsetX / 2 - (k - 1) / 4.0 * (this.getWidth() - offsetX);
                y1 = height - offsetY / 4;
                x2 = this.getWidth() - offsetX / 2 - (k - 1) / 4.0 * (this.getWidth() - offsetX);
                y2 = height - offsetY / 4 - 10;
                g2d.draw(new Line2D.Double(x1, y1, x2, y2));
                String str = "" + (float) (maxX - (k - 1) / 4.0 * (maxX - minX));
                g2d.drawString(str, (float) (x1 - str.length() * 7), height - offsetY / 2 - 3);

                //  on the Y-axis
                x1 = offsetX / 4;
                y1 = (offsetY / 2) + (k - 1) / 4.0 * (maxY - minY) * scaleY;
                x2 = offsetX / 4 + 10;
                y2 = (offsetY / 2) + (k - 1) / 4.0 * (maxY - minY) * scaleY;
                g2d.draw(new Line2D.Double(x1, y1, x2, y2));
                str = "" + (int) (maxY - (k - 1) / 4.0 * (maxY - minY));
                g2d.drawString(str, (int) x1 + 15, (int) y1 + 5);

                //min X
                str = "" + (int) minX;
                g2d.drawString(str, str.length() * 4 + offsetX / 2, height - offsetY / 2);
                //min Y
                str = "" + (int) minY;
                g2d.drawString(str, offsetX / 2, height - offsetY / 2 - 20);
                BasicStroke stroke = new BasicStroke(1, BasicStroke.JOIN_MITER, BasicStroke.CAP_BUTT);
                g2d.setStroke(stroke);
                g2d.draw(new Line2D.Double(0, height, maxX / 500 * scaleX + offsetX, height - maxY / 500 * scaleY - offsetY));
            }
        } else {
            g2d.draw(new Line2D.Double(maxXc, height - offsetY / 4, maxXc, height
                    - offsetY / 4 - 10));
            String str = "" + (int) maxX;
            g2d.drawString(str, (float) (maxXc - str.length() * 10), height - offsetY
                    / 2 - 3);
            g2d.draw(new Line2D.Double(offsetX / 4, offsetY / 2, offsetX / 4 + 10,
                    offsetY / 2));
            str = "" + (int) maxY;
            g2d.drawString(str, offsetX / 2 + 15, (int) (offsetY / 2 + 5));
        }

        // deckungsbetrag line
        g2d.setColor(Color.ORANGE);
        if (deckungsbetrag != 0) {
            double x1, x2, y1, y2;
            if (minY > minX * deckungsbetrag) {
                x1 = minY / deckungsbetrag;
                y1 = minY;
            } else {
                x1 = minX;
                y1 = minX * deckungsbetrag;
            }
            if (maxY > maxX * deckungsbetrag) {
                x2 = maxX;
                y2 = maxX * deckungsbetrag;
            } else {
                x2 = maxY / deckungsbetrag;
                y2 = maxY;
            }
            g2d.setColor(Color.YELLOW);
            g2d.draw(new Line2D.Double(scaledX(x1), scaledY(y1),
                    scaledX(x2), scaledY(y2)));
        }

        //draw selected
        if (selectedP != null) {
            g2d.setColor(Color.DARK_GRAY);
            Ellipse2D eli = new Ellipse2D.Float((float) (selectedP.getX() - 5), (float) (selectedP.getY() - 5), 10, 10);
            g2d.draw(eli);
            g2d.setColor(Color.CYAN);
            g2d.fill(eli);
        }

    }

    private double scaledX(double x) {

        return (x - minX) * (scaleX) + offsetX / 4;
    }

    private double scaledY(double y) {
        return getHeight() - (y - minY) * scaleY - offsetY / 2;
    }

    public void paintLine(Graphics g) {
        double y = deckungsbetrag;
        Graphics2D g2d = (Graphics2D) g;
        if (deckungsbetrag != 0 && deckungsbetrag < newY(maxY)) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            g2d.draw(new Line2D.Double(newX(0), newY(y), deckungsbetrag * ((maxX - minX) * scaleX + offsetX / 2), newY(y)));
        }
    }

    public Collection<Curve> getCurves() {
        return curves;
    }

    public void resetSelectedPoint() {
        this.selectedP = null;
        this.selectedPreal = null;
        this.repaint();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        int distance = 5;
        boolean is = false;
        Point p = new Point();
        Point p2 = new Point();
        JComponent component = (JComponent) e.getSource();
        p.setLocation(e.getX(), e.getY());

        for (Curve c : curves) {
            for (Point pc : c.getCoordinates()) {
                p2.setLocation((pc.getX() - minX) * scaleX + offsetX / 2, this.getHeight() - (pc.getY() - minY) * scaleY - offsetY / 2);
                if (p2.distance(p) < distance) {
                    ToolTipManager.sharedInstance().setInitialDelay(0);
                    component.setToolTipText(c.getName() + " | Time(H):" + (float) (Math.floor(pc.getX() / 60 * 100) / 100.0) + " | Cost:"
                            + (int) pc.getY() + "");
                    is = true;


                }
            }
        }
        if (!is) {
            ToolTipManager.sharedInstance().setInitialDelay(500);
            component.setToolTipText(null);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        int distance = 8;
        Point p = new Point();
        Point p2 = new Point();
        p.setLocation(e.getX(), e.getY());
        for (Curve c : curves) {
            if (c.getName().equals("T/C-TRADEOFF") || c.getName().equals("T/C + Deckungsbetrag") ) {
             
        if (c != null) {
            for (Point pc : c.getCoordinates()) {
                p2.setLocation((pc.getX() - minX) * scaleX + offsetX / 2, this.getHeight() - (pc.getY() - minY) * scaleY - offsetY / 2);
                if (p2.distance(p) < distance) {
                    summary = "<html><b>"+c.getName()
                            + "</b><br></br><i>time: </i>" + (float) (Math.floor(pc.getX() / 60 * 100) / 100.0)
                            + " hours<br></br><i>cost: </i>" + pc.getY() + " $";
                    selectedP = new Point(p2);
                    selectedPreal = new Point(pc);
                    this.repaint();
                }

            }
        }
            }
        }

    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}
